; Journeymen's Essencial Tool
#include <Constants.au3>
#include <GUIConstants.au3>
#Include <GuiComboBox.au3>
#Include <Array.au3>
#include <WindowsConstants.au3>
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#Include <GuiToolTip.au3>
#include <Misc.au3>

Opt("TrayOnEventMode",1)
Opt("TrayMenuMode",1)   ; Default tray menu items (Script Paused/Exit) will not be shown.
Opt("WinTitleMatchMode", 2)

if _Singleton("JET",1) = 0 Then
    Msgbox(0,"Warning","An instance of jet is already running")
    Exit
EndIf

Global $psout;
Global $pserr;
Global $history[11][2];
Global $inputCombo;
Global $icon;
Global $aumacros[100][2];
Global $aumacroCount;
Global $launches[1000][2];
Global $sorted_launches[1000];
Global $launchesFullPath[1000];
Global $launchCount;

Global $Clipboard[10];
Global $ClipboardTop = 0;

Refresh()
LoadINI()

HotKeySet("^!b", "OpenAutoItPrompt")	;Ctrl-Alt-b
HotKeySet("^!s", "ShowOutput")			;Ctrl-Alt-s
HotKeySet("^!e", "ShowError")			;Ctrl-Alt-e
HotKeySet("^!q", "Quit")				;Ctrl-Alt-q

HotKeySet('^!+c', 'CopyPush') ;Ctrl-Alt-Shift-C
HotKeySet('^!+v', 'PastePop') ;Ctrl-Alt-Shift-V

$prompt = TrayCreateItem("Prompt...")
TrayItemSetOnEvent(-1,"OpenAutoItPrompt")
TrayCreateItem("")
$exit_item = TrayCreateItem("Exit")
TrayItemSetOnEvent(-1,"Quit")

While 1
    Sleep(100)
WEnd

Func MakeAllWindowsTransparent($alpha)
	$var = WinList()

	For $i = 1 to $var[0][0]
		If StringInStr($var[$i][0], "SideBar") == 0 And StringInStr($var[$i][0], "NexusFile") == 0 And StringInStr($var[$i][0], "Program Manager") == 0 Then WinSetTrans($var[$i][0], "", $alpha)
	Next
EndFunc

Func Quit()
	Exit 0
EndFunc

Func CopyPush()
	;MsgBox(4096, 'CopyPush', '')
	Send('^c')
	Sleep(100)
	$Clipboard[$ClipboardTop] = ClipGet()
	$ClipboardTop = mod($ClipboardTop + 1, 10)
EndFunc

; Show at the given coordinates (x, y) the popup menu (hMenu) which belongs to a given GUI window (hWnd)
Func TrackPopupMenu($hWnd, $hMenu, $x, $y)
	$return = DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 256, "int", $x, "int", $y, "hwnd", $hWnd, "ptr", 0)
	return $return[0]
EndFunc   ;==>TrackPopupMenu

Func PastePop()
	Local $menu_item[10]
	$pos = MouseGetPos()
	$dummy = GUICreate("jet-paste-dummy")
  $contextmenu = GUICtrlCreateContextMenu()
	For $i = 0 to 9
		$menu_item[$i] = GUICtrlCreateMenuItem($Clipboard[$i], $contextmenu)
	Next
	WinSetTrans("jet-paste-dummy", "", 0)
	GUISetState()
	$chosen_item = TrackPopupMenu($dummy, GUICtrlGetHandle($contextmenu), $pos[0], $pos[1])
	;MsgBox(4096, "chosen item", $chosen_item)
	GUIDelete($dummy)
	For $i = 0 to 9
		If $chosen_item = $menu_item[$i] Then
			ClipPut($Clipboard[$i])
			Send('^v')
			ExitLoop
		EndIf
	Next
EndFunc

Func Refresh()
	LoadAutoItMacros()
	LoadLaunches()
	LoadAndSetHotkeys()
EndFunc

Func HotKeyRun()
	$file = FileOpen(@ScriptDir & "\hotkeys\" & @HotKeyPressed, 0)
	$cmd = FileReadLine($file)
	FileClose($file)
	Execute($cmd)
EndFunc

Func LoadAndSetHotkeys()	
	If FileExists(@ScriptDir & "\hotkeys") == False Then Return
		
	$search = FileFindFirstFile(@ScriptDir & "\hotkeys\*")  

	If $search = -1 Then Return

	While 1
		$filename = FileFindNextFile($search) 
		If @error Then ExitLoop
		
		$attrib = FileGetAttrib(@ScriptDir & "\hotkeys\" & $filename)
		If StringInStr($attrib, "D") Then ContinueLoop

		$file = FileOpen(@ScriptDir & "\hotkeys\" & $filename, 0)
		$filecontent = FileReadLine($file)
		FileClose($file)
		HotKeySet($filename, "HotKeyRun")
	WEnd

	FileClose($search)
EndFunc

Func SearchProgramsDir($path)
	$search = FileFindFirstFile($path & "\*")  

	If $search = -1 Then Return

	While 1
		$filename = FileFindNextFile($search) 
		If @error Then ExitLoop
			
		$attrib = FileGetAttrib($path & "\" & $filename)
		If StringInStr($attrib, "D") Then SearchProgramsDir($path & "\" & $filename)
		
		$launches[$launchCount][0] = $filename; & "   [" & $path & "]"
		$launchesFullPath[$launchCount] = $path & "\" & $filename
		
		$launchCount += 1
	WEnd

	FileClose($search)
EndFunc

Func LoadLaunches()
	$launchCount = 0

	If FileExists(@ScriptDir & "\launches") == False Then Return
		
	$search = FileFindFirstFile(@ScriptDir & "\launches\*.*")  

	If $search = -1 Then Return

	While 1
		$filename = FileFindNextFile($search) 
		If @error Then ExitLoop
			
		$attrib = FileGetAttrib(@ScriptDir & "\launches\" & $filename)
		If StringInStr($attrib, "D") Then ContinueLoop
		
		$launches[$launchCount][0] = $filename; & "   [" & @ScriptDir & "\launches]"
		$launchesFullPath[$launchCount] = @ScriptDir & "\launches\" & $filename
		
		$launchCount += 1
	WEnd

	FileClose($search)
	
	;MsgBox(4096, '1', $launchCount)
	
	; @ProgramsDir
	SearchProgramsDir(@ProgramsDir);
	;MsgBox(4096, '2', $launchCount)

	; @ProgramsCommonDir
	SearchProgramsDir(@ProgramsCommonDir);
	;MsgBox(4096, '3', $launchCount)
	
	; @DesktopDir
	;SearchProgramsDir(@DesktopDir);
	;MsgBox(4096, '4', $launchCount)
	
	; @DesktopCommonDir
	;SearchProgramsDir(@DesktopCommonDir);
	;MsgBox(4096, '5', $launchCount)
	
	For $i = 0 To $launchCount-1
        $launches[$i][1] = $i
    Next	
	
	_ArraySort($launches, 0, 0, $launchCount-1)
	
	For $i = 0 To $launchCount-1
		$sorted_launches[$i] = $launches[$i][0]
    Next	
	;_ArrayDisplay($sorted_launches)
EndFunc

Func LoadAutoItMacros()
	$aumacroCount = 0
	
	If FileExists(@ScriptDir & "\macros") == False Then Return
		
	$search = FileFindFirstFile(@ScriptDir & "\macros\*")  

	If $search = -1 Then Return

	While 1
		$filename = FileFindNextFile($search) 
		If @error Then ExitLoop
		
		$attrib = FileGetAttrib(@ScriptDir & "\macros\" & $filename)
		If StringInStr($attrib, "D") Then ContinueLoop
			
		$aumacros[$aumacroCount][0] = $filename
		$file = FileOpen(@ScriptDir & "\macros\" & $filename, 0)
		$filecontent = FileReadLine($file)
		FileClose($file)
		$aumacros[$aumacroCount][1] = $filecontent
		
		$aumacroCount += 1
	WEnd

	FileClose($search)
EndFunc

Func LoadINI()
	For $i = 0 To 9
        $history[$i][0] = "cmd" & $i
		$history[$i][1] = ""
    Next
	$history[10][0] = "top"
	$history[10][1] = 0
	$tmp = IniReadSection(@ScriptDir & "\jet.ini", "history")
	if @error then Return
	For $i = 0 To 10
		$history[$i][0] = $tmp[$i+1][0]
		$history[$i][1] = $tmp[$i+1][1]
	Next
EndFunc

Func SaveINI()
	IniWriteSection(@ScriptDir & "\jet.ini", "history", $history, 0)
EndFunc

Func ExpandTextToRegExp($text)
	$regex = ".*"
	For $i = 1 To StringLen($text)
		$regex &= (StringMid($text, $i, 1) & ".*")
	Next
	;MsgBox(4096, 'regex', $regex)
	return $regex
EndFunc

Func IsVisible($handle)
  If BitAnd( WinGetState($handle), 2 ) Then 
    Return 1
  Else
    Return 0
  EndIf
EndFunc

Func SupplyComboItems($text)
	_GUICtrlComboBox_ResetContent($inputCombo)
	If $text == "" Then
		return False
	EndIf
	$comboList = $text & "|"
	
	$search_text = ExpandTextToRegExp($text)

	; history
	For $i = $history[10][1]-1 To $history[10][1]-10 Step -1
		If StringRegExp($history[Mod($i+20,10)][1], $text, 0) == 1 Then
			$comboList &= ($history[Mod($i+20,10)][1] & "|")
		EndIf
	Next
	; gw windows
	If StringLeft($text, 2) = 'gw' Then
		$var = WinList()
		For $i = 1 to $var[0][0]
  		; Only display visble windows that have a title
  		If $var[$i][0] <> "" AND IsVisible($var[$i][1]) Then
				$comboList &= ('gw ' & $var[$i][0] & "|")
  		EndIf
		Next	
	EndIf
	; launches
	For $i = 0 To $launchCount-1
		If StringInStr($sorted_launches[$i], $text) <> 0 Then
			$comboList &= ($sorted_launches[$i] & "|")			
		EndIf
	Next
	For $i = 0 To $launchCount-1
		If StringInStr($sorted_launches[$i], $text) == 0 And StringRegExp($sorted_launches[$i], $search_text, 0) == 1 Then
			$comboList &= ($sorted_launches[$i] & "|")			
		EndIf
	Next
	GUICtrlSetData($inputCombo, $comboList)
	;MsgBox(4096, 'comboList', $comboList)
	If $comboList == "" Then
		return False
	Else
		return True
	EndIf
EndFunc

Func FilterEnter()
	HotKeySet("{ENTER}")
	Send("{TAB}{ENTER}")
EndFunc

Func ShowPrompt()
	Local $shell = "AutoIt3"

	HotKeySet("{ENTER}", "FilterEnter")
	
	If WinExists("Journeymen's Essential Tool") Then
		WinActivate("Journeymen's Essential Tool")
		Return ""
	EndIf
	
	$promptDlg = GUICreate("Journeymen's Essential Tool(" & $shell & ")", 640, 24, -1, -1, BitOr($WS_CAPTION, $WS_POPUP), BitOr($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
	$inputCombo = GUICtrlCreateCombo("", 20, 2, 580, 20, -1)
	$icon = GUICtrlCreateIcon(@ScriptFullPath, -1, 2, 4, 16, 16)
	
	SupplyComboItems("")
	
	WinSetTrans("Journeymen's Essential Tool", "", 0)
	
	$on_time = TimerInit()

	GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")
	
	$button = GUICtrlCreateButton("Go!", 600, 2, 40, 20, $BS_DEFPUSHBUTTON)
	GUISetState(@SW_SHOW)

	$result = ""
	While 1
		$msg = GUIGetMsg()
		
		$trans = TimerDiff($on_time) / 3
		If $trans > 127 Then $trans = 127
		WinSetTrans("Journeymen's Essential Tool", "", $trans)

		If Not WinActive("Journeymen's Essential Tool") Then
			$msg = $GUI_EVENT_CLOSE
		EndIf
		
		Select
			Case $msg = $button
				$result = _GUICtrlComboBox_GetEditText($inputCombo)
				ExitLoop 

			Case $msg = $GUI_EVENT_CLOSE
				ExitLoop
		EndSelect
  
		If $msg = $GUI_EVENT_CLOSE Then ExitLoop
	Wend		
	$off_time = TimerInit()
	While 1
		$trans = 127 - TimerDiff($off_time) / 3
		If $trans < 0 Then ExitLoop 
		WinSetTrans("Journeymen's Essential Tool", "", $trans)
	Wend

	ToolTip("")
	GUIDelete($promptDlg)
	
	HotKeySet("{ENTER}")
	Return $result
EndFunc

Func WM_COMMAND($hWnd, $iMsg, $iwParam, $ilParam)
    Local $hWndFrom, $iIDFrom, $iCode, $hWndCombo, $cb_index, $cb_text
    If Not IsHWnd($inputCombo) Then $hWndCombo = GUICtrlGetHandle($inputCombo)
    $hWndFrom = $ilParam
    $iIDFrom = BitAND($iwParam, 0xFFFF) ; Low Word
    $iCode = BitShift($iwParam, 16) ; Hi Word
    Switch $hWndFrom
        Case $inputCombo, $hWndCombo
            Switch $iCode
                Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed_GUICtrlComboBox_ResetContent
                    ; no return value
                Case $CBN_DBLCLK ; Sent when the user double-clicks a string in the list box of a combo box
                    ; no return value
                Case $CBN_DROPDOWN ; Sent when the list box of a combo box is about to be made visible
                    ; no return value
                Case $CBN_EDITCHANGE ; Sent after the user has taken an action that may have altered the text in the edit control portion of a combo box
					ToolTip("")
					$edit_text = _GUICtrlComboBox_GetEditText($inputCombo)
					If SupplyComboItems($edit_text) Then
						_GUICtrlComboBox_ShowDropDown($inputCombo, True)
					Else
						_GUICtrlComboBox_ShowDropDown($inputCombo, False)
					EndIf
					_GUICtrlComboBox_SetEditText($inputCombo, $edit_text)
                    ; no return value
                Case $CBN_EDITUPDATE ; Sent when the edit control portion of a combo box is about to display altered text
                    ; no return value
                Case $CBN_ERRSPACE ; Sent when a combo box cannot allocate enough memory to meet a specific request
                    ; no return value
                Case $CBN_KILLFOCUS ; Sent when a combo box loses the keyboard focus
                    ; no return value
                Case $CBN_SELCHANGE ; Sent when the user changes the current selection in the list box of a combo box
					ToolTip("")
					$cur_sel_text = ""
					_GUICtrlComboBox_GetLBText($inputCombo, _GUICtrlComboBox_GetCurSel($inputCombo), $cur_sel_text)
					$launchIndex = _ArrayBinarySearch($sorted_launches, $cur_sel_text, 0, $launchCount-1)
					if @Error == 0 Then
						$win_pos = WinGetPos("Journeymen's Essential Tool")
						ToolTip($launchesFullPath[$launches[$launchIndex][1]], $win_pos[0], $win_pos[1] - 24)
						$shortcut_details = FileGetShortcut($launchesFullPath[$launches[$launchIndex][1]])
						if @Error == 0 Then
							GUICtrlSetImage($icon, $shortcut_details[0])
						Else
							GUICtrlSetImage($icon, $launchesFullPath[$launches[$launchIndex][1]])
						EndIf
					Else
						GUICtrlSetImage($icon, @ScriptFullPath)
					EndIf
                    ; no return value
                Case $CBN_SELENDCANCEL ; Sent when the user selects an item, but then selects another control or closes the dialog box
                    ; no return value
                Case $CBN_SELENDOK ; Sent when the user selects a list item, or selects an item and then closes the list
                    ; no return value
                Case $CBN_SETFOCUS ; Sent when a combo box receives the keyboard focus
                    ; no return value
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

Func OpenAutoItPrompt()
	$command = ShowPrompt()	
	if $command == "" Then
		Return
	EndIf
	
	; Expand macros, if any.
	$command0 = $command
	$macroCount_ = $aumacroCount;
	$macros_ = $aumacros;
	For $i = 0 To $macroCount_-1
		$replaced = StringRegExpReplace($command, "\A\s*" & $macros_[$i][0] & "\s*(.*)\Z", $macros_[$i][1])
		if @Error == 0 Then
			if @Extended > 0 Then
				$command = $replaced
				;MsgBox(4096, $command0, $command)
				ExitLoop
			EndIf
		EndIf
	Next

	Local $psdir, $pid, $launchIndex, $launchCommand
	if $command == "refreshJet" Then
		Refresh()
	ElseIf $command == "help" Then
		Help()
	Else
		$command_and_args = StringSplit($command, " ")
		$launchIndex = _ArrayBinarySearch($sorted_launches, $command, 0, $launchCount-1)
		if @Error <> 0 Then
			;MsgBox(4096, $command_and_args[1], $command)
			$launchIndex = _ArrayBinarySearch($sorted_launches, StringStripWS($command_and_args[1], 3), 0, $launchCount-1)
		Else
			$command_and_args[0] = 1
		EndIf
		;MsgBox(4096, $command, $launchesFullPath[$launches[$launchIndex][1]])
		if @Error == 0 Then
			$args = ""
			If $command_and_args[0] > 1 Then
				$args = _ArrayToString($command_and_args, " ", 2)
			EndIf
			;MsgBox(4096, "debug", """" & "jet.exe"" /AutoIt3ExecuteLine """ & "ShellExecute('" & $launchesFullPath[$launches[$launchIndex][1]] & "', '" & $args & "')""")
			$pid = Run(@ScriptName & " /AutoIt3ExecuteLine """ & "ShellExecute('" & $launchesFullPath[$launches[$launchIndex][1]] & "', '" &  $args & "')""", @SystemDir, @SW_HIDE, $STDOUT_CHILD + $STDERR_CHILD)
			$launchCommand = True
		Else
			;MsgBox(4096, "debug", $command)
			$pid = Run(@ScriptName & " /ErrorStdOut /AutoIt3ExecuteLine """ & $command & """", @SystemDir, @SW_HIDE, $STDOUT_CHILD + $STDERR_CHILD)
			$launchCommand = False
		EndIf
	EndIf
	
	; Read from child's STDOUT and save
	$out = ""
	While 1
		$out &= StdoutRead($pid)
		If @error Then ExitLoop
	Wend
	$psout = $out
	
	; Read from child's STDERR and save
	$out = ""
	While 1
		$out &= StderrRead($pid)
		If @error Then ExitLoop
	Wend
	$pserr = $out

	ShowOutput()

	; Update history, if necessary
	$isAlreadyInHistory = False
	For $i = 0 To 9
		if $history[$i][1] = $command0 Then
			$isAlreadyInHistory = True
			ExitLoop
		EndIf
    Next
	if $isAlreadyInHistory == False And $launchCommand == False Then
		$history[$history[10][1]][1] = $command0
		$history[10][1] = mod($history[10][1] + 1, 10)
	EndIf
	
	SaveINI()
EndFunc

Func ShowOutput()
	if $psout = "" Then Return
	$dlg = GUICreate("JET Output", 800, 600, -1, $WS_EX_TOOLWINDOW)
	$edit = GUICtrlCreateEdit($psout, -1, -1, 800, 600, $ES_AUTOVSCROLL+$WS_VSCROLL+$ES_READONLY)
	GUISetState(@SW_SHOW)

	While 1
		$msg = GUIGetMsg()
		
		If $msg = $GUI_EVENT_CLOSE Then ExitLoop
	Wend
		
	GUIDelete($dlg)	
EndFunc

Func ShowError()
	if $pserr = "" Then Return
	$dlg = GUICreate("JET Error", 800, 600, -1, $WS_EX_TOOLWINDOW)
	$edit = GUICtrlCreateEdit($pserr, -1, -1, 800, 600, $ES_AUTOVSCROLL+$WS_VSCROLL+$ES_READONLY)
	GUISetState(@SW_SHOW)

	While 1
		$msg = GUIGetMsg()
		
		If $msg = $GUI_EVENT_CLOSE Then ExitLoop
	Wend
		
	GUIDelete($dlg)	
EndFunc

Func Help()
	MsgBox(8192, 'JET help',  _
			'To open a prompt: Ctrl+Alt+B' & @CR &  _
			'To see an output window: Ctrl+Alt+S' & @CR &  _
			'To see an error window: Ctrl+Alt+E' & @CR &  _
			'To quit: Ctrl+Alt+Q' & @CR &  _
			@CR &  _
			'[CopyPush]: Ctrl+Alt+Shift+C  [PastePop]: Ctrl+Alt+Shift+V' & @CR &  _
			@CR &  _
			'To reread hotkeys/launches/macros: input "refreshJet" in the prompt' & @CR &  _
			'To see this help message: input "help" in the prompt' & @CR &  _
			@CR &  _
			'[Symbols for macro]:    ^ Ctrl    ! Alt    + Shift    # Win')
EndFunc
